/* 
 * File: KeyboardHandler
 * Created: 13-JUN-2011
 * Author: Colin Gall-McDaid
 * 
 * Description: Android keyboard handler. Hooks up to the View (through the
 * OnKeyListener) from which the keyboard events are received and stores the current
 * state of each key for polling. Also keeps a list of KeyEvent instances. Must be 
 * synchronized as it will receive events from UI thread as well as being polled 
 * from the main game loop thread.
 */
package com.gallsoft.games.framework.impl;

import java.util.ArrayList;
import java.util.List;

import com.gallsoft.games.framework.Pool;
import com.gallsoft.games.framework.Pool.PoolObjectFactory;
import com.gallsoft.games.framework.Input.KeyEvent;

import android.view.View;
import android.view.View.OnKeyListener;


public class KeyboardHandler implements OnKeyListener {

    private boolean[] pressedKeys = new boolean[128];
    private Pool<KeyEvent> keyEventPool;
    private List<KeyEvent> keyEventsBuffer = new ArrayList<KeyEvent>();
    private List<KeyEvent> keyEvents = new ArrayList<KeyEvent>();
    
    /**
     * Constructor
     * 
     * @param view the Android view
     */
    public KeyboardHandler(View view) {
        PoolObjectFactory<KeyEvent> factory = new PoolObjectFactory<KeyEvent>() {
            
            public KeyEvent createObject() {
                return new KeyEvent();
            }
        };
        
        keyEventPool = new Pool<KeyEvent>(factory, 100);
        view.setOnKeyListener(this);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
    }

    /**
     * Implementation of the OnKeyListener.onKey() interface method which gets
     * called each time the View receives a new key event.
     * 
     * @param v the Android view
     * @param keyCode the key causing the event on the Android device
     * @param event the type of key press (up, down or multiple)
     */
    public boolean onKey(View v, int keyCode, android.view.KeyEvent event) {
        // Multiple actions are not relevant in this context
        if (event.getAction() == android.view.KeyEvent.ACTION_MULTIPLE)
            return false;
        
        // Synchronize with thread
        synchronized (this) {
            // Get KeyEvent object from pool and set values
            KeyEvent keyEvent = keyEventPool.newObject();
            keyEvent.keyCode = keyCode;
            keyEvent.keyChar = (char)event.getUnicodeChar();
            
            // Set framework KeyEvent type base on View KeyEvent type
            if (event.getAction() == android.view.KeyEvent.ACTION_DOWN) {
                keyEvent.type = KeyEvent.KEY_DOWN;
                
                if (keyCode > 0 && keyCode < 127) {
                    pressedKeys[keyCode] = true;
                }
            }
            
            if (event.getAction() == android.view.KeyEvent.ACTION_UP) {
                keyEvent.type = KeyEvent.KEY_UP;
                
                if (keyCode > 0 && keyCode < 127) {
                    pressedKeys[keyCode] = false;
                }
            }
            
            // Add KeyEvent to buffer
            keyEventsBuffer.add(keyEvent);
        }
        
        return false;
    }
    
    /**
     * Check if a particular key has been pressed
     * 
     * @param keyCode
     * @return true or false
     */
    public boolean isKeyPressed(int keyCode) {
        if (keyCode < 0 || keyCode > 127)
            return false;
            
        return pressedKeys[keyCode];        
    }
    
    /**
     * Get key events from buffer. Synchronized as called from different
     * thread.
     * 
     * @return list of key events in buffer
     */
    public List<KeyEvent> getKeyEvents() {
        synchronized (this) {
            int len = keyEvents.size();
            
            for (int i = 0; i < len; i++) {
                keyEventPool.free(keyEvents.get(i));
            }
            
            keyEvents.clear();
            keyEvents.addAll(keyEventsBuffer);
            keyEventsBuffer.clear();
            
            return keyEvents;
        }
    }
}
